﻿using System;
using System.IO.Pipes;
using WeDonekRpc.IOBuffer;
using WeDonekRpc.IOBuffer.Interface;
using WeDonekRpc.PipeServer.Config;
using WeDonekRpc.PipeServer.Log;

namespace WeDonekRpc.PipeServer.Client
{
    /// <summary>
    /// 客户端
    /// </summary>
    internal class PipeClient
    {
        private readonly NamedPipeServerStream _Socket = null;
        private string _ClientName;
        /// <summary>
        /// 当前正在接收的包信息
        /// </summary>
        private Model.DataPageInfo _CurrentPage;
        public string PipeName
        {
            get;
        }
        internal PipeClient (string name)
        {
            this.PipeName = name;//Access to the path is denied.
            this._Socket = new NamedPipeServerStream(name, PipeDirection.InOut, -1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, PipeConfig.ReceiveBufferSize, PipeConfig.SendBufferSize);
        }

        private readonly Guid _ClientId = Guid.NewGuid();

        public string ClientName
        {
            get
            {
                return this._ClientName;
            }
        }
        /// <summary>
        /// 客户端ID
        /// </summary>
        internal Guid ClientId => this._ClientId;

        private Interface.IClientEvent _Client;

        /// <summary>
        /// 客户端
        /// </summary>
        internal Interface.IClientEvent Client
        {
            set => this._Client = value;
        }
        /// <summary>
        /// 异步发送字节流
        /// </summary>
        /// <param name="buffer"></param>
        private bool _Send (ISocketBuffer buffer)
        {
            if (!this._Socket.IsConnected)
            {
                return false;
            }
            try
            {
                _ = this._Socket.BeginWrite(buffer.Stream, 0, buffer.BufferSize, new AsyncCallback(this._SendCallback), buffer);
                return true;
            }
            catch (Exception e)
            {
                IoLogSystem.AddSendLog(e, this);
                this.Close();
                buffer.Dispose();
                return false;
            }
        }
        internal bool Send (ref Model.DataPage page)
        {
            ISocketBuffer buffer = PipeHelper.GetSendBuffer(ref page);
            return this._Send(buffer);
        }
        internal bool Send (ref Model.DataPage page, uint pageId)
        {
            ISocketBuffer buffer = PipeHelper.GetSendBuffer(ref page, pageId);
            return this._Send(buffer);
        }
        private void _SendCallback (IAsyncResult ar)
        {
            ISocketBuffer buffer = (ISocketBuffer)ar.AsyncState;
            int ver = buffer.Ver;
            try
            {
                this._Socket.EndWrite(ar);
                this._Socket.Flush();
                this._Client.SendPageComplateEvent(buffer.PageId);
            }
            catch (Exception e)
            {
                this._Client.SendPageErrorEvent(buffer.PageId);
                IoLogSystem.AddSendLog(e, this);
                this.Close();
            }
            finally
            {
                buffer.Dispose(ver);
            }
        }
        private ISocketBuffer _Buffer = null;
        private int _Ver = 0;
        private int _Size;
        /// <summary>
        /// 初始化Socket连接
        /// </summary>
        public void InitSocket ()
        {
            this._Size = PipeConfig.BufferSize;
            this._Buffer = BufferCollect.ApplyBuffer(this._Size, ref this._Ver);
            try
            {
                _ = this._Socket.BeginWaitForConnection(this._AcceptCon, null);
            }
            catch (Exception e)
            {
                IoLogSystem.AddInitLog(e, this);
                this._Buffer.Dispose();
                this.Close();
                this._Client.Accept(false);
            }
        }

        private void _AcceptCon (IAsyncResult ar)
        {
            bool isCon = false;
            try
            {
                this._Socket.EndWaitForConnection(ar);
                this._ClientName = this._Socket.GetImpersonationUserName();
                _ = this._Socket.BeginRead(this._Buffer.Stream, 0, this._Buffer.BufferSize, this._Read, null);
                isCon = true;
            }
            catch (Exception e)
            {
                IoLogSystem.AddAcceptLog(e, this);
                this.Close();
            }
            finally
            {
                this._Client.Accept(isCon);
            }
        }


        /// <summary>
        /// 异步回调
        /// </summary>
        /// <param name="evt"></param>
        private void _Read (IAsyncResult evt)
        {
            try
            {
                int size = this._Socket.EndRead(evt);
                if (size == 0)
                {
                    this.Close();
                    return;
                }
                if (!PipeHelper.SplitPage(this._Buffer.Stream, ref size, ref this._CurrentPage, this))
                {
                    this.Close();
                    return;
                }
                this._Buffer.SetBufferSize(this._Size);
            }
            catch (Exception e)
            {
                IoLogSystem.AddReceiveLog(e, this);
                this.Close();
            }
            finally
            {
                if (this._Socket.IsConnected)
                {
                    try
                    {
                        _ = this._Socket.BeginRead(this._Buffer.Stream, 0, this._Buffer.BufferSize, new AsyncCallback(this._Read), null);
                    }
                    catch (Exception e)
                    {
                        IoLogSystem.AddReceiveLog(e, this);
                        this.Close();
                    }
                }
            }
        }
        /// <summary>
        /// 接收数据完成时调用
        /// </summary>
        internal void ReceiveComplate ()
        {
            if (this._CurrentPage != null)
            {
                this._Client.AllotEvent(this._CurrentPage);
                this._CurrentPage = null;
            }
        }
        /// <summary>
        /// 关闭连接
        /// </summary>
        internal void Close ()
        {
            this._Client?.ConCloseEvent();
            this._Buffer?.Dispose(this._Ver);
            if (this._Socket.IsConnected)
            {
                this._Socket.Close();
            }
            this._Socket.Dispose();
        }
    }
}
